package com.wei.czz.framework.common.manager;

import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.constant.EntityConstant;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.dto.common.UploadDto;
import com.wei.czz.common.dto.common.multiDict.MultiDictDto;
import com.wei.czz.common.dto.common.multiDict.MultiDictPageEnumDto;
import com.wei.czz.common.easyexcel.listener.ObjectUploadListener;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.common.importTask.ImportTaskTypeEnum;
import com.wei.czz.common.enums.common.multiDIct.MultiDictEnum;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.common.multiDict.MultiDictFormVo;
import com.wei.czz.common.vo.common.multiDict.MultiDictUploadVo;
import com.wei.czz.common.vo.common.multiDict.MultiDictVo;
import com.wei.czz.framework.common.entity.MultiDictEntity;
import com.wei.czz.framework.common.helper.CacheHelper;
import com.wei.czz.framework.common.helper.ImportTaskHelper;
import com.wei.czz.framework.common.service.MultiDictService;
import com.wei.czz.framework.common.service.SnowflakeService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-06-08 22:27:26
 * className: MultiDictManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class MultiDictManager {

    private static final Logger log = LoggerFactory.getLogger(MultiDictManager.class);

    private final MultiDictService multiDictService;

    private final SnowflakeService snowflakeService;

    private final ImportTaskHelper importTaskHelper;

    private final CacheHelper cacheHelper;


    public void saveMultiDict(MultiDictVo multiDictVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        // 映射
        MultiDictEntity multiDict = CopyUtils.map(multiDictVo, MultiDictEntity.class);
        multiDict.setId(null)
                .setParentPath(multiDictVo.getParentPath() + multiDictVo.getParentId() + Constant.SPLIT)
                .setStatus(CommonEnum.ZERO.getValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userPo.getUsername())
                .setCreateUserId(userPo.getUserId());
        /*
            保存级联字典
         */
        multiDictService.saveOrEdit(multiDict);

    }

    public UploadDto<MultiDictUploadVo> uploadMultiDict(ObjectUploadListener<MultiDictUploadVo> objectUploadListener,
                                                        Boolean mustAll) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        ImportTaskTypeEnum importTaskTypeEnum = ImportTaskTypeEnum.MULTI_DICT;

        // 解析成功列表
        List<MultiDictUploadVo> successList = objectUploadListener.getSuccessList();
        // 解析成功映射
        Map<Integer, MultiDictUploadVo> successMap = objectUploadListener.getSuccessMap();
        // 解析失败列表
        List<MultiDictUploadVo> failList = objectUploadListener.getFailList();

        /*
            判断是否有可导入数据
         */
        if (successList.isEmpty()) {
            // 没有解析成功数据，直接返回
            return importTaskHelper.buildSyncUploadDto(importTaskTypeEnum, successList, failList, MultiDictUploadVo.class);
        }

        // 收集父级主键
        List<Long> parentIdList = CopyUtils.mapList(successList, MultiDictUploadVo::getParentNo);

        /*
            获取父级字典
         */
        List<MultiDictEntity> parentMultiDictList = multiDictService.getList(parentIdList);
        // list 转 map
        Map<Long, MultiDictEntity> multiDictMap = CopyUtils.listToMap(parentMultiDictList, MultiDictEntity::getId, Function.identity());

        // 待保存列表
        List<MultiDictEntity> saveList = new ArrayList<>();
        // 延后处理的列表
        List<MultiDictUploadVo> lastOptList = new ArrayList<>();
        //
        Map<String, Integer> typeMap = MultiDictEnum.getNameMap();

        Iterator<MultiDictUploadVo> iterator = successList.iterator();
        while (iterator.hasNext()) {
            MultiDictUploadVo multiDictUploadVo = iterator.next();

            // 获取字典类型
            Integer type = typeMap.get(multiDictUploadVo.getTypeName());
            if (Objects.isNull(type)) {
                log.info("字典类型不存在。serial={} typeName={}", multiDictUploadVo.getSerial(), multiDictUploadVo.getTypeName());
                multiDictUploadVo.setErrorMessage("字典类型不存在");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }

            Integer status;
            if ("可用".equals(multiDictUploadVo.getStatusName())) {
                status = CommonEnum.ZERO.getValue();
            } else if ("禁用".equals(multiDictUploadVo.getStatusName())) {
                status = CommonEnum.ONE.getValue();
            } else {
                log.info("状态值错误。serial={} statusName={}", multiDictUploadVo.getSerial(), multiDictUploadVo.getStatusName());
                multiDictUploadVo.setErrorMessage("状态值错误");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }

            Integer defaultStatus;
            if ("是".equals(multiDictUploadVo.getDefaultStatusName())) {
                defaultStatus = CommonEnum.ZERO.getValue();
            } else if ("否".equals(multiDictUploadVo.getDefaultStatusName())) {
                defaultStatus = CommonEnum.ONE.getValue();
            } else {
                log.info("默认状态值错误。serial={} defaultStatusName={}", multiDictUploadVo.getSerial(), multiDictUploadVo.getDefaultStatusName());
                multiDictUploadVo.setErrorMessage("默认状态值错误");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }


            // 从数据库存储的级联字典中获取父级字典
            MultiDictEntity parentMultiDict = multiDictMap.get(multiDictUploadVo.getParentNo());

            /*
                不是以下这三种情况，则判定该条数据错误
                1、父级‘级联字典’不是0
                2、父级’级联字典‘不是数据库已有的‘级联字典’
                3、父级‘级联字典’不是同一批上传的‘级联字典’
             */
            if (!CommonEnum.ZERO.getLongValue().equals(multiDictUploadVo.getParentNo()) && Objects.isNull(parentMultiDict)
                    && !successMap.containsKey(multiDictUploadVo.getParentNo().intValue())) {
                log.info("父级‘级联字典’不存在。serial={} parentNo={}", multiDictUploadVo.getSerial(),
                        multiDictUploadVo.getParentNo());
                multiDictUploadVo.setErrorMessage("父级‘级联字典’不存在");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }

            Long parentId;
            String parentPath;
            if (CommonEnum.ZERO.getLongValue().equals(multiDictUploadVo.getParentNo())) {
                parentId = CommonEnum.ZERO.getLongValue();
                parentPath = CommonEnum.ZERO.getValue() + Constant.SPLIT;
            } else if (Objects.nonNull(parentMultiDict)) {
                parentId = parentMultiDict.getId();
                parentPath = parentMultiDict.getParentPath() + parentMultiDict.getId() + Constant.SPLIT;
            } else {
                // 父级‘级联字典’还未遍历到，延后处理
                lastOptList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }

            Long id = snowflakeService.get();
            log.info("生成主键 serial={} id={}", multiDictUploadVo.getSerial(), id);

            // 构建保存对象
            MultiDictEntity saveMultiDict = new MultiDictEntity();
            saveMultiDict.setId(id)
                    .setParentId(parentId)
                    .setParentPath(parentPath)
                    .setType(type)
                    .setName(multiDictUploadVo.getName())
                    .setValue(multiDictUploadVo.getValue())
                    .setSort(multiDictUploadVo.getSort())
                    .setStatus(status)
                    .setDefaultStatus(defaultStatus)
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId());
            saveList.add(saveMultiDict);

            // 增加新映射，方便下面使用
            multiDictMap.put(multiDictUploadVo.getSerial().longValue(), saveMultiDict);

        }

        // 处理延后处理的父级‘级联字典’
        for (MultiDictUploadVo multiDictUploadVo : lastOptList) {
            // 获取字典类型
            Integer type = typeMap.get(multiDictUploadVo.getTypeName());
            if (Objects.isNull(type)) {
                log.info("延后处理数据，字典类型不存在。serial={} typeName={}", multiDictUploadVo.getSerial(), multiDictUploadVo.getTypeName());
                multiDictUploadVo.setErrorMessage("字典类型不存在");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 继续下一个
                continue;
            }

            Integer status;
            if ("可用".equals(multiDictUploadVo.getStatusName())) {
                status = CommonEnum.ZERO.getValue();
            } else if ("禁用".equals(multiDictUploadVo.getStatusName())) {
                status = CommonEnum.ONE.getValue();
            } else {
                log.info("延后处理数据，状态值错误。serial={} statusName={}", multiDictUploadVo.getSerial(), multiDictUploadVo.getStatusName());
                multiDictUploadVo.setErrorMessage("状态值错误");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }

            Integer defaultStatus;
            if ("是".equals(multiDictUploadVo.getDefaultStatusName())) {
                defaultStatus = CommonEnum.ZERO.getValue();
            } else if ("否".equals(multiDictUploadVo.getDefaultStatusName())) {
                defaultStatus = CommonEnum.ONE.getValue();
            } else {
                log.info("延后处理数据，默认状态值错误。serial={} defaultStatusName={}", multiDictUploadVo.getSerial(), multiDictUploadVo.getDefaultStatusName());
                multiDictUploadVo.setErrorMessage("默认状态值错误");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 从迭代器中移除
                iterator.remove();
                // 继续下一个
                continue;
            }

            // 从数据库存储的级联字典中获取父级字典
            MultiDictEntity parentMultiDict = multiDictMap.get(multiDictUploadVo.getParentNo());
            if (Objects.isNull(parentMultiDict)) {
                log.info("延后处理数据，父级‘级联字典’不存在。serial={} parentNo={}", multiDictUploadVo.getSerial(),
                        multiDictUploadVo.getParentNo());
                multiDictUploadVo.setErrorMessage("父级‘级联字典’不存在");
                // 添加到失败列表
                failList.add(multiDictUploadVo);
                // 继续下一个
                continue;
            }

            Long parentId = parentMultiDict.getId();
            String parentPath = parentMultiDict.getParentPath() + parentMultiDict.getId() + Constant.SPLIT;

            // 构建保存对象
            MultiDictEntity saveMultiDict = new MultiDictEntity();
            saveMultiDict.setId(snowflakeService.get())
                    .setParentId(parentId)
                    .setParentPath(parentPath)
                    .setType(type)
                    .setName(multiDictUploadVo.getName())
                    .setValue(multiDictUploadVo.getValue())
                    .setSort(multiDictUploadVo.getSort())
                    .setStatus(status)
                    .setDefaultStatus(defaultStatus)
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId());
            saveList.add(saveMultiDict);

            // 重新添加到解析成功列表中
            successList.add(multiDictUploadVo);
        }
        if (!lastOptList.isEmpty()) {
            // 解析成功列表重新排序
            successList.sort(Comparator.comparingInt(MultiDictUploadVo::getSerial));
        }

        /*
            生成导入记录
         */
        UploadDto<MultiDictUploadVo> uploadDto = importTaskHelper.buildSyncUploadDto(importTaskTypeEnum, successList,
                failList, MultiDictUploadVo.class);

        if (saveList.isEmpty()) {
            log.info("映射成功数据为空");
            return uploadDto;
        }
        if (mustAll && !failList.isEmpty()) {
            log.info("存在解析错误数据，不保存解析成功数据");
            return uploadDto;
        }

        /*
            保存解析成功数据
         */
        multiDictService.batchSave(saveList);

        return uploadDto;
    }

    public PageDto<MultiDictDto> getMultiDictPageList(MultiDictFormVo multiDictFormVo) {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        /*
            请求表单参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.MULTI_DICT);
        cacheHelper.cache(cacheKey, multiDictFormVo);

        /*
            分页获取级联字典
         */
        PageDto<MultiDictEntity> pageDto = multiDictService.getPageList(multiDictFormVo);
        if (pageDto.isEmpty()) {
            return pageDto.rebuild();
        }

        List<MultiDictEntity> multiDictList = pageDto.getList();

        // 如果参数包含检索内容，则只做分页查询
        boolean bool = StringUtils.isNotBlank(multiDictFormVo.getWord());
        if (bool) {
            // 列表映射
            List<MultiDictDto> multiDictDtoList = CopyUtils.mapList(multiDictList, multiDict -> {
                MultiDictDto multiDictDto = CopyUtils.map(multiDict, MultiDictDto.class);
                multiDictDto.setTypeName(MultiDictEnum.getName(multiDict.getType()));
                return multiDictDto;
            });
            // 封装返回
            return pageDto.rebuild(multiDictDtoList);
        }

        /*
            构造级联字典树
         */
        List<MultiDictDto> resultList = this.buildMultiDictTree(multiDictList, null);

        // 封装返回
        return pageDto.rebuild(resultList);
    }

    public MultiDictPageEnumDto getMultiDictPageParam() {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        /*
            请求表单参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.MULTI_DICT);
        MultiDictFormVo multiDictFormVo = cacheHelper.get(cacheKey, MultiDictFormVo.class);

        List<NameValue<Integer>> defaultStatusEnumList = new ArrayList<>(2);
        defaultStatusEnumList.add(new NameValue<>("是", 0));
        defaultStatusEnumList.add(new NameValue<>("否", 1));

        List<NameValue<Integer>> statusEnumList = new ArrayList<>(2);
        statusEnumList.add(new NameValue<>("可用", 0));
        statusEnumList.add(new NameValue<>("禁用", 1));

        MultiDictPageEnumDto multiDictPageEnumDto = new MultiDictPageEnumDto();
        multiDictPageEnumDto.setTypeEnumList(MultiDictEnum.getList())
                .setDefaultStatusEnumList(defaultStatusEnumList)
                .setStatusEnumList(statusEnumList)
                .setMultiDictFormVo(multiDictFormVo);
        return multiDictPageEnumDto;
    }

    public List<MultiDictDto> getMultiDictList(Integer type) {

        /*
            获取级联字典
         */
        List<MultiDictEntity> multiDictList = multiDictService.findList(type, null);

        return multiDictList.stream()
                .sorted((o1, o2) -> {
                    /*
                        排序方式
                        1、根据父级路径中分隔符个数进行排序，升序排序
                        2、根据创建时间进行排序，升序排序
                     */
                    int count1 = this.statSplitCount(o1.getParentPath());
                    int count2 = this.statSplitCount(o2.getParentPath());
                    int res = Integer.compare(count1, count2);
                    if (res != 0) {
                        return res;
                    }
                    return o1.getCreateTime().compareTo(o2.getCreateTime());
                })
                .map(multiDict -> {
                    MultiDictDto multiDictDto = CopyUtils.map(multiDict, MultiDictDto.class);
                    multiDictDto.setTypeName(MultiDictEnum.getName(multiDict.getType()));
                    return multiDictDto;
                })
                .collect(Collectors.toList());
    }

    public MultiDictDto getMultiDictTree(Integer type, Long id) {

        /*
            获取级联字典
         */
        List<MultiDictEntity> multiDictList = multiDictService.findList(type, null);

         /*
            构造级联字典树
         */
        List<MultiDictDto> multiDictDtoList = this.buildMultiDictTree(multiDictList, id);
        TreeSet<MultiDictDto> treeSet = new TreeSet<>(multiDictDtoList);

        MultiDictDto multiDictDto = new MultiDictDto();
        multiDictDto.setId(CommonEnum.ZERO.getStringValue())
                .setParentPath(StringUtils.EMPTY)
                .setName("顶级字典")
                .setSort(CommonEnum.ZERO.getValue())
                .setDisabled(Boolean.FALSE)
                .setChildren(treeSet);
        return multiDictDto;
    }

    public void updateMultiDict(MultiDictVo multiDictVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取级联字典
         */
        MultiDictEntity multiDict = multiDictService.get(multiDictVo.getId());
        // 判断是否更新父级
        boolean updateParent = !multiDict.getParentId().equals(multiDictVo.getParentId());

        MultiDictEntity updateMultiDict = new MultiDictEntity();
        updateMultiDict.setName(multiDictVo.getName())
                .setValue(multiDictVo.getValue())
                .setSort(multiDictVo.getSort())
                .setDefaultStatus(multiDictVo.getDefaultStatus())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());

        List<MultiDictEntity> updateList = new ArrayList<>();
        if (updateParent) {
            // 新父级路径
            String parentPath = multiDictVo.getParentPath() + multiDictVo.getParentId() + Constant.SPLIT;

            updateMultiDict.setParentId(multiDictVo.getParentId())
                    .setParentPath(parentPath);
            // 收集
            updateList.add(updateMultiDict);

            /*
                获取子级联字典
             */
            List<MultiDictEntity> childrenList = multiDictService.findChildrenList(
                    multiDict.getParentPath() + multiDict.getId() + Constant.SPLIT
            );
            for (MultiDictEntity childrenMultiDict : childrenList) {
                parentPath = childrenMultiDict.getParentPath();
                // 部门路径替换
                parentPath = parentPath.replaceFirst(multiDict.getParentPath(), multiDictVo.getParentPath());

                updateMultiDict = new MultiDictEntity();
                updateMultiDict.setId(childrenMultiDict.getId())
                        .setParentPath(parentPath)
                        .setUpdateTime(date)
                        .setUpdateUser(userPo.getUsername())
                        .setUpdateUserId(userPo.getUserId());
                updateList.add(updateMultiDict);
            }
        }

        /*
            修改级联字典
         */
        multiDictService.batchUpdate(updateList);

    }

    public void deleteMultiDict(List<Long> idList) {

        /*
            获取子、子孙级联字典
         */
        List<MultiDictEntity> multiDictList = multiDictService.findChildrenList(idList);
        for (MultiDictEntity multiDict : multiDictList) {
            idList.add(multiDict.getId());
        }

        /*
            删除级联字典
         */
        multiDictService.delete(idList);

    }


    /**
     * 构造级联字典树
     * @param multiDictList 字典字典对象列表
     * @param id            禁用状态主键
     * @return 级联字典树
     */
    private List<MultiDictDto> buildMultiDictTree(List<MultiDictEntity> multiDictList, Long id) {
        Map<String, MultiDictDto> multiDictMap = new HashMap<>();
        List<MultiDictDto> lastMultiDictList = new ArrayList<>();

        // 禁用状态主键
        String disableId = Optional.ofNullable(id).map(Objects::toString).orElse(StringUtils.SPACE);

        for (MultiDictEntity multiDict : multiDictList) {
            // 映射
            MultiDictDto multiDictDto = CopyUtils.map(multiDict, MultiDictDto.class);
            multiDictDto.setTypeName(MultiDictEnum.getName(multiDict.getType()))
                    .setDisabled(Boolean.FALSE);

            if (multiDictDto.getId().equals(disableId) || CommonEnum.ONE.getValue().equals(multiDictDto.getStatus())
                    || multiDictDto.getParentPath().contains(disableId)) {
                multiDictDto.setDisabled(Boolean.TRUE);
            }

            multiDictMap.put(multiDictDto.getId(), multiDictDto);

            // 获取父菜单
            MultiDictDto parentMultiDictDto = multiDictMap.get(multiDictDto.getParentId());
            if (Objects.isNull(parentMultiDictDto)) {
                // 延后处理
                lastMultiDictList.add(multiDictDto);
                continue;
            }

            TreeSet<MultiDictDto> children = parentMultiDictDto.getChildren();
            if (Objects.isNull(children)) {
                children = new TreeSet<>();
                parentMultiDictDto.setChildren(children);
            }
            children.add(multiDictDto);
        }
        List<MultiDictDto> resultList = new ArrayList<>();
        for (MultiDictDto multiDictDto : lastMultiDictList) {
            // 获取父菜单
            MultiDictDto parentMultiDictDto = multiDictMap.get(multiDictDto.getParentId());
            if (Objects.isNull(parentMultiDictDto)) {
                // 放入结果列表
                resultList.add(multiDictDto);
                continue;
            }

            TreeSet<MultiDictDto> children = parentMultiDictDto.getChildren();
            if (Objects.isNull(children)) {
                children = new TreeSet<>();
                parentMultiDictDto.setChildren(children);
            }
            children.add(multiDictDto);
        }

        return resultList;
    }

    /**
     * 统计父级路径中，分隔符的数量
     * @param parentPath 父级路径
     * @return 分隔符的数量
     */
    private int statSplitCount(String parentPath) {
        int count = 0;

        int index = 0;
        while((index = parentPath.indexOf(Constant.SPLIT,index)) != -1) {
            index = index + Constant.SPLIT.length();
            count++;
        }

        return count;
    }
}
